Render As You Fetchパターン
@housecor: Problem: Fetching in useEffect means React components render, then fetch. This can lead to slow network waterfalls. Solution: Fetch as you render. Use react-query's prefetching (prefetch in the parent) or use @remix_run which does this by default via nested routes.
https://pbs.twimg.com/media/FUF8tZDUEAEEGeY.jpg
このアンチパターン(Fetch Then Render)を克服するために生まれた
裏にある理論
要点
キャッシュキーの生成や管理手法は自由で、WeakMapだったり文字列だったり様々 この辺に秩序をもたらす設計論もある
コンポーネントはマウント時に、キャッシュマネージャーを介し透過的にAPIレスポンスを読み取る
キャッシュがあるとき: キャッシュからレスポンスを得る
キャッシュがないとき: データフェッチして、その結果をキャッシュに格納する 得られるメリット
キャッシュの有無に関わらず、コンポーネントのレンダリングの結果は同じになる
コンポーネントの純粋度が高まる = レンダリングの予測性を高める
↓みたいなのを考えなくてよくなる
データフェッチ→レスポンス返ってくるまでsuspend→返ってきたらレンダリング再開 ローディングなどの処理をそのコンポーネントの外に任せることができる
型レベルでローディングの状態を追い出すことができ、宣言的に非同期データを扱える
「コンポーネントがpromiseをthrowする = 継続を取ってこれる例外である」
コード例
showing in one function how to solve 5 issues that would’ve taken me a week to figure out (if at all 🙏🏼)
https://scrapbox.io/files/641cd857ad2680001c74491b.png
このuseQueryに全てが詰め込まれている
1. API呼び出し
3. refetch on window focus
4. place holder
5. stale time
6. loading, error
オプトインする形でuseQueryのオプションにsuspense: trueを追加するとSuspenseモードになる 従来との差分
old
親がマウント時にuseEffectでデータフェッチして子にpropsで流していた
レスポンスが返ってくるまではフラグ管理などで子コンポーネントをマウントしないなどで制御する
痛み
考慮する状態が増える
マウント時のレスポンスは空
フェッチ後に保存される
検索条件を変えたら再度フェッチ
並列レンダリング制御が難しい
リクエスト数の増加やパフォーマンスの低下
複数APIを扱う時にローディングやAPIを呼び出す順番やレスポンスの正規化で悩む
ローディング管理などでコンポーネント階層が深くなりがち
after
考慮する状態が減る
ローディング中はそもそもレンダリングされない
依存フェッチの仕組みによって、イベントハンドラで手続き的な再フェッチ処理をしなくとも良い
コンポーネント階層を浅くできる
制約
トレードオフ
いつキャッシュを破棄する, いつ明示的に再取得するなど
CRUDの状態遷移などでキャッシュが古くなった場合、開発者は明示的にキャッシュ破棄する必要性がある そうしないとキャッシュ破棄を忘れて古いデータが表示される
管理画面などでは多少速度や利便性を犠牲にしてでも強整合性のほうが向いている場合もある 技術の螺旋を乗り越えてうまれたものなので、一度試してみる価値はある 設計上の利点
コンポーネントの見通しがよくなる
並列レンダリング, ローディング, エラーを親で制御できる
勘所